Skip to content

fix(sdk): make generated SDK docs reproducible to unblock CI - #231

Closed
dodeja wants to merge 1 commit into
mainfrom
fix/sdk-docs-seo-reproducible
Closed

fix(sdk): make generated SDK docs reproducible to unblock CI#231
dodeja wants to merge 1 commit into
mainfrom
fix/sdk-docs-seo-reproducible

Conversation

@dodeja

@dodeja dodeja commented May 29, 2026

Copy link
Copy Markdown
Contributor

Problem

The sdk (24) CI job has failed on every run since May 18 (independent of any recent PR). The job runs npm run docs and then asserts the generated output is committed:

git diff --exit-code -- docs/sdk/reference
test -z "$(git status --porcelain -- docs/sdk/reference)"

#219 ("improve SEO titles and descriptions for SDK reference pages") hand-edited the TypeDoc-generated MDX to add SEO title/description frontmatter on 46 pages. But the generator (typedoc + scripts/postprocess-generated-docs.mjs) only emits a generic title: "Type Alias: X" and no description — so it can no longer reproduce the committed files, and the up-to-date guard fails on every run.

The drift is purely in title/description frontmatter; page bodies regenerate byte-identical.

Fix

Restore reproducibility without reverting the SEO work:

  • seo-overrides.json — the 46 hand-authored {title, description} pairs, keyed by route-relative .mdx path.
  • postprocess-generated-docs.mjs — loads the sidecar and applies the overrides when writing frontmatter, so npm run docs regenerates the committed files exactly.

Going forward, SEO metadata is edited in seo-overrides.json rather than in the generated MDX (which docs:clean wipes on every run).

Verification

Locally, on a clean checkout: npm ci && npm run docsgit diff --exit-code -- docs/sdk/reference returns no diff and no untracked files (both CI assertions pass).

Note

This is unrelated to the Claude workflow PR (#230); it's a pre-existing main breakage. Worth merging on its own to turn CI green again.

🤖 Generated with Claude Code

Greptile Summary

This PR fixes a broken CI job by extracting the 46 hand-edited SEO title/description values from the generated MDX files into a versioned seo-overrides.json sidecar, then applying them in the post-processing script so npm run docs regenerates the committed output byte-for-byte.

  • seo-overrides.json — new file holding all hand-authored frontmatter pairs keyed by route-relative MDX path; future SEO edits go here instead of in generated files.
  • postprocess-generated-docs.mjs — loads the sidecar at startup and merges title/description into YAML frontmatter in ensureFrontmatter, falling back to the auto-derived title when no override exists.

Confidence Score: 4/5

Safe to merge; the script change is minimal and the sidecar JSON is straightforward — the only risks are cosmetic edge cases in the post-processor.

The core logic is correct and the fix squarely addresses the described CI breakage. The two minor concerns are: JSON.parse for the sidecar file has no error handling (a malformed JSON produces a cryptic message with no filename), and ensureFrontmatter's early return silently skips SEO overrides whenever a file already carries frontmatter, which could be surprising if TypeDoc's output ever changes.

sdks/typescript-sdk/scripts/postprocess-generated-docs.mjs — the JSON loading and the early-return guard in ensureFrontmatter are worth a quick second look.

Important Files Changed

Filename Overview
sdks/typescript-sdk/scripts/postprocess-generated-docs.mjs Loads seo-overrides.json at startup and applies title/description overrides in ensureFrontmatter; JSON.parse lacks error handling and the early-return guard silently skips overrides if frontmatter already exists.
sdks/typescript-sdk/seo-overrides.json New sidecar file with 46 hand-authored title/description pairs keyed by route-relative MDX path; content is straightforward and internally consistent.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["npm run docs"] --> B["TypeDoc generates MDX files\n(no frontmatter)"]
    B --> C["postprocess-generated-docs.mjs"]
    C --> D["Load seo-overrides.json\n(keyed by route-relative path)"]
    D --> E["For each .mdx file"]
    E --> F["rewriteMdxLinks()\nrelative → absolute Mintlify routes"]
    F --> G{"content starts\nwith ---?"}
    G -- "Yes (already has frontmatter)" --> H["Return unchanged\n⚠ SEO overrides NOT applied"]
    G -- "No" --> I["Look up relKey in SEO_OVERRIDES"]
    I --> J{"override\nexists?"}
    J -- "Yes" --> K["Use override.title\nAppend override.description"]
    J -- "No" --> L["Derive title from H1 / filename"]
    K --> M["Write frontmatter + body to file"]
    L --> M
    M --> N["git diff --exit-code\n✅ CI passes"]
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
sdks/typescript-sdk/scripts/postprocess-generated-docs.mjs:27-29
If `seo-overrides.json` contains a syntax error (stray comma, misquoted string, etc.), `JSON.parse` throws a bare `SyntaxError` with a character-offset message and no filename context, making it hard to diagnose during CI. Wrapping with a try-catch surfaces the file path alongside the parse error.

```suggestion
const SEO_OVERRIDES = (() => {
  if (!fs.existsSync(seoOverridesPath)) return {};
  try {
    return JSON.parse(fs.readFileSync(seoOverridesPath, 'utf8'));
  } catch (err) {
    throw new Error(`Failed to parse ${seoOverridesPath}: ${err.message}`);
  }
})();
```

### Issue 2 of 2
sdks/typescript-sdk/scripts/postprocess-generated-docs.mjs:80-88
**SEO overrides silently skipped when frontmatter already exists**

`ensureFrontmatter` returns early on line 81 if the file already starts with `---\n`, so if TypeDoc ever starts emitting its own YAML frontmatter (or if a developer runs `npm run docs` without `docs:clean` and the file happens to have been written with frontmatter from a prior run that omitted overrides), the SEO title/description is silently not applied and no error is raised. The CI diff guard would eventually catch the mismatch, but only after committing the wrong output. This is low risk given the current TypeDoc output, but the silent skip could be confusing in the future if the generator changes.

Reviews (1): Last reviewed commit: "fix(sdk): make generated SDK docs reprod..." | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

The `sdk (24)` CI job runs `npm run docs` then asserts
`git diff --exit-code -- docs/sdk/reference`. PR #219 hand-edited the
TypeDoc-generated MDX to add SEO titles/descriptions, but the generator
(typedoc + postprocess-generated-docs.mjs) couldn't reproduce them, so
the up-to-date guard has failed on every run since May 18.

Restore reproducibility without losing the SEO work:
- Add seo-overrides.json — the 46 hand-authored title/description pairs,
  keyed by route-relative .mdx path.
- Teach postprocess-generated-docs.mjs to apply those overrides when
  writing frontmatter, so `npm run docs` regenerates the committed files
  byte-for-byte.

Verified: `npm run docs` now leaves docs/sdk/reference with no diff.
Edit seo-overrides.json (not the generated MDX) for future SEO tweaks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented May 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
api Ready Ready Preview, Comment May 29, 2026 9:04pm

Request Review

Comment on lines +27 to +29
const SEO_OVERRIDES = fs.existsSync(seoOverridesPath)
? JSON.parse(fs.readFileSync(seoOverridesPath, 'utf8'))
: {};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 If seo-overrides.json contains a syntax error (stray comma, misquoted string, etc.), JSON.parse throws a bare SyntaxError with a character-offset message and no filename context, making it hard to diagnose during CI. Wrapping with a try-catch surfaces the file path alongside the parse error.

Suggested change
const SEO_OVERRIDES = fs.existsSync(seoOverridesPath)
? JSON.parse(fs.readFileSync(seoOverridesPath, 'utf8'))
: {};
const SEO_OVERRIDES = (() => {
if (!fs.existsSync(seoOverridesPath)) return {};
try {
return JSON.parse(fs.readFileSync(seoOverridesPath, 'utf8'));
} catch (err) {
throw new Error(`Failed to parse ${seoOverridesPath}: ${err.message}`);
}
})();
Prompt To Fix With AI
This is a comment left during a code review.
Path: sdks/typescript-sdk/scripts/postprocess-generated-docs.mjs
Line: 27-29

Comment:
If `seo-overrides.json` contains a syntax error (stray comma, misquoted string, etc.), `JSON.parse` throws a bare `SyntaxError` with a character-offset message and no filename context, making it hard to diagnose during CI. Wrapping with a try-catch surfaces the file path alongside the parse error.

```suggestion
const SEO_OVERRIDES = (() => {
  if (!fs.existsSync(seoOverridesPath)) return {};
  try {
    return JSON.parse(fs.readFileSync(seoOverridesPath, 'utf8'));
  } catch (err) {
    throw new Error(`Failed to parse ${seoOverridesPath}: ${err.message}`);
  }
})();
```

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines 80 to 88
function ensureFrontmatter(content, filePath) {
if (content.startsWith('---\n')) return content;
const title = frontmatterTitle(content, filePath);
return `---\ntitle: ${JSON.stringify(title)}\n---\n\n${content}`;
const relKey = path.relative(outputDir, filePath).split(path.sep).join(path.posix.sep);
const override = SEO_OVERRIDES[relKey];
const title = override?.title ?? frontmatterTitle(content, filePath);
const lines = [`title: ${JSON.stringify(title)}`];
if (override?.description) lines.push(`description: ${JSON.stringify(override.description)}`);
return `---\n${lines.join('\n')}\n---\n\n${content}`;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 SEO overrides silently skipped when frontmatter already exists

ensureFrontmatter returns early on line 81 if the file already starts with ---\n, so if TypeDoc ever starts emitting its own YAML frontmatter (or if a developer runs npm run docs without docs:clean and the file happens to have been written with frontmatter from a prior run that omitted overrides), the SEO title/description is silently not applied and no error is raised. The CI diff guard would eventually catch the mismatch, but only after committing the wrong output. This is low risk given the current TypeDoc output, but the silent skip could be confusing in the future if the generator changes.

Prompt To Fix With AI
This is a comment left during a code review.
Path: sdks/typescript-sdk/scripts/postprocess-generated-docs.mjs
Line: 80-88

Comment:
**SEO overrides silently skipped when frontmatter already exists**

`ensureFrontmatter` returns early on line 81 if the file already starts with `---\n`, so if TypeDoc ever starts emitting its own YAML frontmatter (or if a developer runs `npm run docs` without `docs:clean` and the file happens to have been written with frontmatter from a prior run that omitted overrides), the SEO title/description is silently not applied and no error is raised. The CI diff guard would eventually catch the mismatch, but only after committing the wrong output. This is low risk given the current TypeDoc output, but the silent skip could be confusing in the future if the generator changes.

How can I resolve this? If you propose a fix, please make it concise.

@dodeja

dodeja commented May 29, 2026

Copy link
Copy Markdown
Contributor Author

Closing: this was cut from a stale local main (3a7fa77). The real current main (07e5e4c) already regenerated the SDK reference docs in #227, removing #219's hand-authored SEO frontmatter — so the generated output is now generic and fully reproducible, and sdk (24) is green on main.

This PR's seo-overrides.json approach would re-introduce that SEO frontmatter and re-break reproducibility, so it's no longer the right fix. Closing as obsolete.

@dodeja dodeja closed this May 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant